/*
 *  lexer.c
 *  project5
 *
 *  Created by Theron Rodgers
 *  Description: Program to recieve input from a file or terminal and split input into types and tokens.
 *  Functions pertaining to linked list adapted from example given in class
 */

#include "lexer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

//Declaration of list type
struct list {
	struct token *head;
	struct token *tail;
};
//Prototypes for included functions
void tokenizer(char *tokens, struct list *ll);
struct list *new_list();
int find_char(char *tok);
void print_list(FILE *fp, struct list *ll);
//Declaration of variable to track line numbers
static int linecount = 0;

//Function to read input and divide it into tokens
void scan( FILE *fp, struct list *ll) {

	char line[LINE_MAX];
	char *tok;
	
	//splits lines of input into tokens
	while (fgets(line, LINE_MAX, fp) != NULL)	{
	
		tok = strtok (line, " \n");
		//creates token objects from input
		while(tok != NULL)	{

			tokenizer(tok, ll);
			tok = strtok (NULL, " ");
		}
		linecount++;
	}
}
//Prints a list in formatted output
void print_list(FILE *fp, struct list *ll)	{

	struct token *tok;
	//Travels through list and prints each node
	for( tok = ll->head; tok != NULL; tok = tok->next){
		//Prints tokens of type INTEGER
		if(tok->type == INTEGER)	{
		
			fprintf(fp, "The value of this token is %d and its type is INTEGER\n", tok->v.i);
		}
		//Prints tokens of type FLOAT
		else if (tok->type == FLOAT)	{
		
			fprintf(fp, "The value of this token is %f and its type is FLOAT\n", tok->v.f);
		}
		//Prints tokens of type REGISTER
		else if (tok->type == REGISTER)	{
		
			if(tok->v.r == R1)	{
			
				fprintf(fp, "The value of this token is R1 and its type is REGISTER\n");
			}
			if(tok->v.r == A)	{
			
				fprintf(fp, "The value of this token is A and its type is REGISTER\n");
			}
			if(tok->v.r == R2)	{
			
				fprintf(fp, "The value of this token is R2 and its type is REGISTER\n");
			}
			if(tok->v.r == R3)	{
			
				fprintf(fp, "The value of this token is R3 and its type is REGISTER\n");
			}
			if(tok->v.r == R4)	{
			
				fprintf(fp, "The value of this token is R4 and its type is REGISTER\n");
			}
			if(tok->v.r == R5)	{
			
				fprintf(fp, "The value of this token is R5 and its type is REGISTER\n");
			}
			if(tok->v.r == R6)	{
			
				fprintf(fp, "The value of this token is R6 and its type is REGISTER\n");
			}
			if(tok->v.r == R7)	{
			
				fprintf(fp, "The value of this token is R7 and its type is REGISTER\n");
			}
			
		}
		//Prints tokens of type OPCODE
		else if (tok->type == OPCODE)	{
		
			if(tok->v.op ==BEGIN)	{
			
				fprintf(fp, "The value of this token is BEGIN and its type is OPCODE\n");
			}
			if(tok->v.op ==END)	{
			
				fprintf(fp, "The value of this token is END and its type is OPCODE\n");
			}
			if(tok->v.op ==MOVE)	{
			
				fprintf(fp, "The value of this token is MOVE and its type is OPCODE\n");
			}
			if(tok->v.op ==ANL)	{
			
				fprintf(fp, "The value of this token is ANL and its type is OPCODE\n");
			}
			if(tok->v.op ==ORL)	{
			
				fprintf(fp, "The value of this token is ORL and its type is OPCODE\n");
			}
			if(tok->v.op ==XRL)	{
			
				fprintf(fp, "The value of this token is XLR and its type is OPCODE\n");
			}
			if(tok->v.op ==SWAP)	{
			
				fprintf(fp, "The value of this token is SWAP and its type is OPCODE\n");
			}
			if(tok->v.op ==CLR)	{
			
				fprintf(fp, "The value of this token is CLR and its type is OPCODE\n");
			}
			if(tok->v.op ==SETB)	{
			
				fprintf(fp, "The value of this token is SETB and its type is OPCODE\n");
			}
			if(tok->v.op ==SJMP)	{
			
				fprintf(fp, "The value of this token is SJMP and its type is OPCODE\n");
			}
			if(tok->v.op ==JZ)	{
			
				fprintf(fp, "The value of this token is JZ and its type is OPCODE\n");
			}
			if(tok->v.op ==ADDC)	{
			
				fprintf(fp, "The value of this token is ADDC and its type is OPCODE\n");
			}
			if(tok->v.op ==MULT)	{
			
				fprintf(fp, "The value of this token is MULT and its type is OPCODE\n");
			}
			if(tok->v.op ==DIV)	{
			
				fprintf(fp, "The value of this token is DIV and its type is OPCODE\n");
			}
			if(tok->v.op ==DISP)	{
			
				fprintf(fp, "The value of this token is DISP and its type is OPCODE\n");
			}
			if(tok->v.op ==INPT)	{
			
				fprintf(fp, "The value of this token is INPT and its type is OPCODE\n");
			}
		}
	}
}

//Constructor for a new token object
struct token *new_token(union value v_new, enum lexeme type_new, int linenum_new)	{
	//Allocates memory
	struct token *t = (struct token *) malloc (sizeof (struct token));
	//Adds data to each component of token struct
	t->v = v_new;
	t->type = type_new;
	t->linenum = linenum_new;
	t->next = NULL;
}
//Constructor for a new list object
struct list *new_list ()
{
	//Allocates memory
	struct list *ll = (struct list *) malloc (sizeof (struct list));
	//Creates two nodes
	ll->head = NULL;
	ll->tail = NULL;

	return ll;
}
//Frees the memory in a token
void delete_token (struct token *n)
{
	free (n);
}

//Deletes an entire list
void delete_list (struct list *ll)
{
	struct token *pos = ll->head;
	struct token *old;

	/* free all the nodes in the list */
	while (pos != NULL) {
		old = pos;

		pos = pos->next;

		delete_token (old);
	}

	/* free the list itself */
	free (ll);
}

//Adds a token to list
void list_pushback (struct list *ll, union value u, enum lexeme type, int linenum)	{
	if (ll->head == NULL) {	
		/* list is empty, so start at beginning */
		ll->head = ll->tail = new_token(u, type, linenum);
	}
	else {
		ll->tail = ll->tail->next = new_token(u, type, linenum);
	}
}

//Determines what type of token input is and adds to list
void tokenizer (char *futureTokens, struct list *stuff)	{

	union value newvalue;
	enum lexeme type;
	int linenum;
	//Catches input if is of type REGISTER
	if (strcmp(futureTokens, "A") == 0)	{
		//Creates a token with required information and adds it to list
		newvalue.r = A;
		type = REGISTER;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);

	}
	else if (strcmp(futureTokens, "R1") ==0)	{
		
		newvalue.r = R1;
		type = REGISTER;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "R2") ==0)	{
	
		newvalue.r = R2;
		type = REGISTER;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "R3") == 0)	{
	
		newvalue.r = R3;
		type = REGISTER;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "R4") == 0)	{
	
		newvalue.r = R4;
		type = REGISTER;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "R5") == 0)	{
	
		newvalue.r = R5;
		type = REGISTER;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "R6") == 0)	{
	
		newvalue.r = R6;
		type = REGISTER;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "R7") == 0)	{
	
		newvalue.r = R7;
		type = REGISTER;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	//Catches input if type OPCODE
	else if (strcmp(futureTokens, "BEGIN") == 0)	{
	
		newvalue.op = BEGIN;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "END") == 0)	{

		newvalue.op = END;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "MOVE") == 0)	{
	
		newvalue.op = MOVE;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "ANL") == 0)	{
	
		newvalue.op = ANL;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "ORL") == 0)	{
	
		newvalue.op = ORL;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "XLR") == 0)	{
	
		newvalue.op = XRL;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "SWAP") == 0)	{
	
		newvalue.op = SWAP;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "CLR") == 0)	{
	
		newvalue.op = CLR;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "SETB") == 0)	{
	
		newvalue.op = SETB;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "JZ") == 0)	{
	
		newvalue.op = JZ;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "ADDC") == 0)	{
	
		newvalue.op = ADDC;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "MULT") == 0)	{
	
		newvalue.op = MULT;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "DIV") == 0)	{
	
		newvalue.op = DIV;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "DISP") == 0)	{
	
		newvalue.op = DISP;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	else if (strcmp(futureTokens, "INPT") == 0)	{
	
		newvalue.op = INPT;
		type = OPCODE;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	//Catches input if of type float
	else if (LONG_MIN < (float)*futureTokens < LONG_MAX  && (int)strchr(futureTokens, '.') != NULL && strchr(futureTokens, '.') ==strrchr(futureTokens, '.') && find_decimal(futureTokens) == 1)	{
		
		newvalue.f =atof(futureTokens);
		type = FLOAT;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	//Catches input if of type int
	else if (INT_MIN <= (int)*futureTokens <= INT_MAX && find_char(futureTokens) == 1)	{

		newvalue.i =  atoi(futureTokens);
		type = INTEGER;
		linenum = linecount;
		list_pushback(stuff, newvalue, type, linenum);
	}
	//Returns an error message for all other inputs
	else {
		fprintf(stderr, "The token at line %d is not formatted correctly.\n", linecount);
	}
}

//Searches input to determine if it is an int
int find_char(char *tok)	{

	int maybe;
	int i, j,k,l;
	int size = strlen(tok);
	char test;
	//Searches for ASCII characters before number set
	for(i = 0; i<size; i++)	{
		test = '!';
		for(k = 33; k < 45; k++)	{
			maybe = (int)strchr(tok, test);
			if (maybe != NULL) {
				return 0;
			}
			test++;
		}
	}
		for(i = 0; i<size; i++)	{
		test = '.';
		for(k = 0; k < 1; k++)	{
			maybe = (int)strchr(tok, test);
			if (maybe != NULL) {
				return 0;
			}
			test++;
		}
	}
	//Searches for ASCII characters after number set
	for(j = 0; j<size; j++)	{
		test = ':';
		for(l = 58; l < 126; l++)	{
			maybe = (int)strchr(tok, test);
			if (maybe != NULL) {
				return 0;
			}
			test++;
		}
	}
	return 1;
}
//counts all the decimals in a token
int find_decimal(char *tok)	{

	int maybe;
	int i, j,k,l;
	int size = strlen(tok);
	char test;
	//Searches for ASCII characters before number set
	for(i = 0; i<size; i++)	{
		test = '!';
		for(k = 33; k < 45; k++)	{
			maybe = (int)strchr(tok, test);
			if (maybe != NULL) {
				return 0;
			}
			test++;
		}
	}
	if ((int)strchr(tok, '/') != NULL)	{
		return 0;
	}
	//Searches for ASCII characters after number set
	for(j = 0; j<size; j++)	{
		test = ':';
		for(l = 58; l < 126; l++)	{
			maybe = (int)strchr(tok, test);
			if (maybe != NULL) {
				return 0;
			}
			test++;
		}
	}
	return 1;
}